//
//  CZXMPPManager.m
//  CZWeiXin
//
//  Created by Alvechen on 16/4/10.
//  Copyright © 2016年 alvechen. All rights reserved.
//

#import "CZXMPPManager.h"
//日志打印
#import <DDLog.h>
#import <DDTTYLogger.h>
#import <XMPPLogging.h>

#define CHANG_PWD_id @"change1"

@interface CZXMPPManager ()<XMPPStreamDelegate,XMPPAutoPingDelegate,XMPPRosterDelegate,XMPPIncomingFileTransferDelegate>

///密码
@property (nonatomic,copy) NSString *password;

///成功回调
@property (nonatomic,copy) void(^successHandle)();
///失败回调
@property (nonatomic,copy) void(^errorHandle)(NSError *);

@property (nonatomic,strong) XMPPJID  *fileFrom;

@end

@implementation CZXMPPManager

+ (instancetype)shareInstance{
    
    static CZXMPPManager *instance = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        instance = [self new];
    });
    
    return instance;
}

- (XMPPStream *)xmppStream{
    
    if (_xmppStream != nil) {
        return _xmppStream;
    }
    //实例化
    
    _xmppStream = [[XMPPStream alloc] init];
    //主机名
    _xmppStream.hostName = kCZ_HOST_NAME;
    _xmppStream.hostPort = kCZ_HOST_PORT;
    
    //设置代理 指定代理对象和代理执行所在队列
    [_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    
    //添加打印器
    [DDLog addLogger:[DDTTYLogger sharedInstance] withLogLevel:XMPP_LOG_FLAG_SEND_RECV];
    //配置颜色
    [[DDTTYLogger sharedInstance] setColorsEnabled:YES];//颜色支持
    [[DDTTYLogger sharedInstance] setForegroundColor:[UIColor redColor] backgroundColor:[UIColor whiteColor] forFlag:XMPP_LOG_FLAG_SEND];
    [[DDTTYLogger sharedInstance] setForegroundColor:[UIColor blueColor] backgroundColor:[UIColor whiteColor] forFlag:XMPP_LOG_FLAG_RECV_POST];
    
    
    //激活模块
    [self setupModules];
    
    
    return _xmppStream;
}

/**
 * 配置模块
 */
- (void)setupModules{
    
    //1.生成
    //2.配置
    //3.激活
    
    /************************ 心跳检测 ***************************/
    //1.生成
    self.xmppAutoPing = [[XMPPAutoPing alloc] initWithDispatchQueue:dispatch_get_main_queue()];
    //设置代理- ping的事件回调代理
    [self.xmppAutoPing addDelegate:self delegateQueue:dispatch_get_main_queue()];
    //2.配置
//    //ping频率
//    self.xmppAutoPing.pingInterval = 5;
//    //ping超时时间
//    self.xmppAutoPing.pingTimeout = 5;
    
    //自动响应别人的ping
    self.xmppAutoPing.respondsToQueries = YES;
    
    //3.激活-指定在哪个流上激活
//    [self.xmppAutoPing activate:self.xmppStream];
    
    /************************ 重连 ***************************/
    //1.生成
    self.xmppReconnetc = [[XMPPReconnect alloc] initWithDispatchQueue:dispatch_get_main_queue()];
    //2.配置
    self.xmppReconnetc.autoReconnect = YES;
    self.xmppReconnetc.reconnectDelay = 3;
    
    //3.激活
    [self.xmppReconnetc activate:self.xmppStream];
    
    /************************ 花名册 ***************************/
    
    //1.生成
    //花名册存储器,是一个单例,以后只要是要使用的存储器,都是单例
    XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance];
    
    self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_main_queue()];
    
    [self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
    //2.配置
    //是否自动接受别人对我们的出席订阅(添加我),为NO,需要手动接受
    self.xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = NO;
    //是否自动在流关闭连接时清除数据库用户信息
    self.xmppRoster.autoClearAllUsersAndResources = NO;
    //是否自动获取花名册,如果为NO,则需要自己手动获取
    self.xmppRoster.autoFetchRoster = YES;
    
    //3.激活
    [self.xmppRoster activate:self.xmppStream];
    
    //手动获取花名册
//    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//        
//        [self.xmppRoster fetchRoster];
//    });
    
    /************************ 聊天记录 ***************************/
    //1.生成
    XMPPMessageArchivingCoreDataStorage *messageArchSotrage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
    
    self.xmppMessageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:messageArchSotrage dispatchQueue:dispatch_get_main_queue()];
    //2.配置-不需要配置
    //3.激活
    
    [self.xmppMessageArchiving activate:self.xmppStream];
    
    
    /************************ 文件接收 ***************************/
    //1.生成
    self.xmppIncomingFileTransfer = [[XMPPIncomingFileTransfer alloc] initWithDispatchQueue:dispatch_get_main_queue()];
    
    [self.xmppIncomingFileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];
    //2.配置
//    self.xmppIncomingFileTransfer.disableDirectTransfers = NO;
//    self.xmppIncomingFileTransfer.disableIBB = NO;
//    self.xmppIncomingFileTransfer.disableSOCKS5 = NO;
    
    //是否自动接收文件
    self.xmppIncomingFileTransfer.autoAcceptFileTransfers = NO;
    
    //3.激活
    [self.xmppIncomingFileTransfer activate:self.xmppStream];

    /************************ 文件发送 ***************************/
    //1.生成
    self.xmppOutgoingFileTransfer = [[XMPPOutgoingFileTransfer alloc] initWithDispatchQueue:dispatch_get_main_queue()];
    //2.配置
//    self.xmppOutgoingFileTransfer.disableDirectTransfers = NO;
//    self.xmppOutgoingFileTransfer.disableIBB = NO;
//    self.xmppOutgoingFileTransfer.disableSOCKS5 = NO;
    
    
    //3.激活
    [self.xmppOutgoingFileTransfer activate:self.xmppStream];
    
    /************************ 个人资料头像模块 ***************************/

    //资料卡存储器
    XMPPvCardCoreDataStorage *vCardStorage = [XMPPvCardCoreDataStorage sharedInstance];
    
    //个人资料模板模块
    self.xmppvCardTempModule = [[XMPPvCardTempModule alloc] initWithvCardStorage:vCardStorage dispatchQueue:dispatch_get_main_queue()];
    
    //个人资料头像模块
    self.xmppvCardAvatarModule = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:self.xmppvCardTempModule dispatchQueue:dispatch_get_main_queue()];
    
    //两个模块都激活
    [self.xmppvCardTempModule activate:self.xmppStream];
    [self.xmppvCardAvatarModule activate:self.xmppStream];

    
}

/**
 * 登录
 */
- (void)loginWithAcountName:(NSString *)acountName password:(NSString *)password successHandle:(void(^)())successHandle errorHandle:(void(^)(NSError *error))errorHandle{
    
    //保存密码,在认证时使用
    self.password = password;
    
    //保存成功和失败的回调
    self.successHandle = successHandle;
    self.errorHandle = errorHandle;
    
    //连接到服务器
    
    //创建JID,user:用户名  domain:域 resource:资源
    XMPPJID *aJID = [XMPPJID jidWithUser:acountName domain:kCZ_DOMAIN resource:kCZ_RESOURCE];
    //流连接前,要绑定一个jid
    self.xmppStream.myJID = aJID;
    NSError *error = nil;
    [self.xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error];
    
    if (error) {
        
        NSLog(@"连接服务器error = %@",error);
    }else{
    
        NSLog(@"连接操作完成");
    }
}

/// 注册
- (void)registerWithJID:(XMPPJID *)JID andPassword:(NSString *)password {
    [self.xmppStream setMyJID:JID];
    self.password = password;
    self.xmppNeedRegister = YES;
    
    NSError *error = nil;
    // XMPPStreamTimeoutNone永不超时
    [self.xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error];
    
    if (error) {
        
        NSLog(@"连接服务器error = %@",error);
    }else{
        
        NSLog(@"连接操作完成");
    }
}

/**
 *  修改密码，前提是一定要保持登录状态
 */
- (void)changePasswordWith:(NSString *)username password:(NSString *)password {
    NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"];
    NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"];
    [iq addAttributeWithName:@"type" stringValue:@"set"];
    [iq addAttributeWithName:@"to" stringValue:@"im.zhigong.com"];
    [iq addAttributeWithName:@"id" stringValue:CHANG_PWD_id];//#define CHANG_PWD_id @"change1"确定id唯一就行
    DDXMLNode *usernanmeNode=[DDXMLNode elementWithName:@"username" stringValue:username];//不带@后缀
    DDXMLNode *passwordNode=[DDXMLNode elementWithName:@"password" stringValue:password];//要改的密码
    [query addChild:usernanmeNode];
    [query addChild:passwordNode];
    [iq addChild:query];
    
    /*
        打印下XML流
        <iq type="set" to="im.zhigong.com" id="change1"><query xmlns="jabber:iq:register"><username>zhangsan</username><password>123456</password></query></iq>
     */
    //NSLog(@"iq XML = %@",iq.description);
    
    [self.xmppStream sendElement:iq];
}


#pragma mark - XMPPStreamDelegate

/**
 * socket连接成功
 */
- (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket{

}

/**
 * xml流连接成功
 */
- (void)xmppStreamDidConnect:(XMPPStream *)sender{
    
    if (self.xmppNeedRegister) {
        
        [self.xmppStream registerWithPassword:self.password error:nil];
        
    } else {
        //XML流连接成后,开始登陆(认证)
        NSError *error = nil;
        
        [self.xmppStream authenticateWithPassword:self.password error:&error];
        
        if (error) {
            NSLog(@"认证error = %@",error);
        }
    }
}

/*
 - (NSString *)type
 {
	NSString *type = [self attributeStringValueForName:@"type"];
	if(type)
 return [type lowercaseString];
	else
 return @"available";
 }
 
 - (NSString *)show
 {
	return [[self elementForName:@"show"] stringValue];
 }
 
 - (NSString *)status
 {
	return [[self elementForName:@"status"] stringValue];
 }
 
 - (int)priority
 {
	return [[[self elementForName:@"priority"] stringValue] intValue];
 }
 
 - (int)intShow
 {
	NSString *show = [self show];
	
	if([show isEqualToString:@"dnd"])
 return 0;
	if([show isEqualToString:@"xa"])
 return 1;
	if([show isEqualToString:@"away"])
 return 2;
	if([show isEqualToString:@"chat"])
 return 4;
	
	return 3;
 }
 */

/**
 * 认证成功
 */
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender{

    //执行成功回调
    if (self.successHandle) {
        self.successHandle();
    }
    
    //上线(出席)
    XMPPPresence *presence = [XMPPPresence presence];
    
    //改变状态
//    XMPPElement *showE = [XMPPElement elementWithName:@"show" stringValue:@"away"];
//    [presence addChild:showE];
//    
//    //自定义状态文本-在 "在线"状态下无效,只能是在忙碌或不可聊天等下才生效
//    XMPPElement *statusE = [XMPPElement elementWithName:@"status" stringValue:@"我现在很忙啦!"];
//    [presence addChild:statusE];
    
    
    // 发送一个<presence/> 默认值avaliable 在线 是指服务器收到空的presence 会认为是这个
    // status ---自定义的内容，可以是任何的。
    // show 是固定的，有几种类型 dnd(请勿打扰)、xa(离开一段时间)、away(离开)、chat(可聊天)，在方法XMPPPresence 的intShow中可以看到
    // 设置状态文字（在show元素是chat时候是无效的）
    //    //自定义状态文本-在 "在线"状态下无效,只能是在忙碌或不可聊天等下才生效
    [presence addChild:[DDXMLNode elementWithName:@"status" stringValue:@"我现在很忙"]];
    [presence addChild:[DDXMLNode elementWithName:@"show" stringValue:@"xa"]];
    
   
    [self.xmppStream sendElement:presence];
}

/**
 * 认证失败
 */
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
    
    if (error) {
        NSLog(@"认证失败error = %@",error);
    }
    
    NSError *aError = [NSError errorWithDomain:@"CZXMPPManagenrAuth" code:001 userInfo:@{NSLocalizedDescriptionKey:error}];
    
    //执行失败回调
    
    if (self.errorHandle) {
        self.errorHandle(aError);
    }
}

// 注册成功后调用
- (void)xmppStreamDidRegister:(XMPPStream *)sender
{
    NSLog(@"注册 成功 代理调用");
    [[NSNotificationCenter defaultCenter] postNotificationName:kREGIST_RESULT object:@(1)];
}

// 注册失败后调用
- (void)xmppStream:(XMPPStream *)sender didNotRegister:(NSXMLElement *)error
{
    NSLog(@"注册 失败 代理调用 error = %@",error);
    [[NSNotificationCenter defaultCenter] postNotificationName:kREGIST_RESULT object:@(0)];
} 

// 当接收到 <iq /> 标签的内容时,调用该代理方法
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq{
    
    // 我猜的：打印服务器返回的信息
    NSLog(@"发送xml流代理__%@",iq.description);
    
    /*
     
     <iq xmlns="jabber:client" type="result" id="change1" from="ubuntu-dev" to="13333333333@ubuntu-dev/870efdcd"></iq>
     
     */
    
    NSString *iqTypePWD = [[iq attributeForName:@"type"] stringValue];
    
    NSString *iqIDPWD = [[iq attributeForName:@"id"] stringValue];
    
    NSLog(@"iqTypePWD:%@___iqTypePWD:%@",iqTypePWD,iqIDPWD);
    
    if ([iqTypePWD isEqualToString:@"result"] && [iqIDPWD isEqualToString:CHANG_PWD_id]) {
        //进行判断只有type="result" id="change1"时,密码修改成功
        NSLog(@"密码修改成功!");
    }
    
    return YES;
}




#pragma mark - XMPPAutoPingDelegate

/**
 * 发送一个ping
 */
- (void)xmppAutoPingDidSendPing:(XMPPAutoPing *)sender{

}

/**
 * 收到ping的回应
 */
- (void)xmppAutoPingDidReceivePong:(XMPPAutoPing *)sender{


}

/**
 * 超时回调
 */
- (void)xmppAutoPingDidTimeout:(XMPPAutoPing *)sender{

    
}

#pragma mark - XMPPRosterDelegate

/**
 * 收到订阅请求
 */
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence{
    
    //接受
    [sender acceptPresenceSubscriptionRequestFrom:presence.from andAddToRoster:YES];

}
//
///**
// * 开始获取花名册
// */
//- (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender withVersion:(NSString *)version{
//
//}
//
///**
// * 每获取到一条数据就会调,获取到的数据会自动保存到数据库
// */
//- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item{
//
//    
//}
//
///**
// * 花名册获取结束
// */
//- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender{
//
//}
//
///**
// * 订阅关系发生改变的时候
// */
//- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterPush:(XMPPIQ *)iq{
//
//}


#pragma mark - XMPPIncomingFileTransferDelegate

/**
 * 文件接收 邀请
 */
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender didReceiveSIOffer:(XMPPIQ *)offer{

    self.fileFrom = offer.from;
    
    //手动接收文件传输邀请
    [self.xmppIncomingFileTransfer acceptSIOffer:offer];
    
    
}

/**
 * 文件接收成功
 */
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender didSucceedWithData:(NSData *)data named:(NSString *)name{

    //写入沙盒
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    
    //拼接全路径
    NSString *filePath = [path stringByAppendingPathComponent:name];
    
    NSLog(@"%@",filePath);
    
    //写入
    [data writeToFile:filePath atomically:YES];
    
    //由于发送附件不会产生message,在聊天界面要显示一条消息需要自己创建消息 打包到数据库
    
    XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.xmppStream.myJID];
    
    [message addBody:name];
    
    // 判断name是否包含_RECORD，如果包含说明是语音
    if ([name containsString:@"_RECORD"]) {
        [message addSubject:@"RECORD"];
    }else {
        //添加subject,用于识别图片
        [message addSubject:@"IMAGE"];
    }
    
    //message from属性,有非空约束
    [message addAttributeWithName:@"from" stringValue:self.fileFrom.bare];
    //打包入库
    [[XMPPMessageArchivingCoreDataStorage sharedInstance] archiveMessage:message outgoing:NO xmppStream:self.xmppStream];
}

/**
 * 文件接收失败
 */
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender didFailWithError:(NSError *)error{

}


@end
